<?php
/**
 * Forum / board module.
 *
 * @author Martin "DevelX" Jurča
 */
class Forum extends Module {
    protected $name = 'Forum';
    protected $version = '2009-12-18';
    protected $details = array('dependencies' => array(),
            'observes' => array('Forum'));

    const HALLS = 'forumHalls';
    const THREADS = 'forumThreads';
    const POSTS = 'forumPosts';

    protected $hallsTable = array('id' => 'iNP+', 'name' => "v'1024'N",
            'description' => "v'4096'N", 'parent' => 'iN', 'lock' => 'iN',
            'createPostsAllowed' => 'iN', 'editPostsAllowed' => 'iN',
            'deletePostsAllowed' => 'iN', 'icon' => 'i');
    protected $threadsTable = array('id' => 'iNP+', 'name' => "v'1024'N",
            'hall' => 'iN', 'lock' => 'iN', 'status' => 'iN', 'lastResponse' =>
            'iN', 'createPostsAllowed' => 'iN', 'editPostsAllowed' => 'iN',
            'deletePostsAllowed' => 'iN');
    protected $postsTable = array('id' => 'iNP+', 'thread' => 'iN', 'author' =>
            'iN', 'title' => "v'1024'N", 'created' => 'iN', 'lastEdit' => 'iN',
            'text' => "v'8192'N", 'editCount' => 'iN');

    protected $hallRecord = array('table' => self::HALLS, 'data' => array());
    protected $threadRecord = array('table' => self::THREADS, 'data' =>
            array());
    protected $postRecord = array('table' => self::POSTS, 'data' => array());

    private $postsOnPage = 20;
    private $threadsOnPage = 20;

    public function __construct(DBAPI $dbapi, RecordList $recordList) {
        parent::__construct($dbapi, $recordList);
        $this->hallRecord['properties'] = $this->hallsTable;
        $this->threadRecord['properties'] = $this->threadsTable;
        $this->postRecord['properties'] = $this->postsTable;
    }

    public function install() {
        Modules::getModule('Users')->registerRights($this->getName(),
                $this->getRights());
        return $this->dbdriver->createTable(self::HALLS, $this->hallsTable) &&
                $this->dbdriver->createTable(self::THREADS,
                $this->threadsTable) && $this->dbdriver->createTable(
                self::POSTS, $this->postsTable);
    }

    public function uninstall() {
        return $this->dbdriver->dropTable(self::HALLS) &&
                $this->dbdriver->dropTable(self::THREADS) &&
                $this->dbdriver->dropTable(self::POSTS);
    }

    public function initDisplay($param = null) {}

    public function onCreateHall(&$details, $source) {
        if (!$this->canCreateHall()) {
            return;
        }
        $hall = $this->hallRecord;
        $hall['data'] = array('name' => $details['name'], 'description' =>
                $details['description'], 'parent' => $details['parent'],
                'lock' => isset($details['lock']) ? 1 : 0,
                'createPostsAllowed' => isset($details['createPostsAllowed']) ?
                1 : 0, 'editPostsAllowed' =>
                isset($details['editPostsAllowed']) ? 1 : 0,
                'deletePostsAllowed' => isset($details['deletePostsAllowed']) ?
                1 : 0);
        $this->dbapi->save($hall);
    }

    public function onCreateThread(&$details, $source) {
        if (!$this->canCreateThread($details['hall'])) {
            return;
        }
        $hall = $this->hallRecord;
        $hall['data']['id'] = $details['hall'];
        $this->dbapi->refresh($hall);
        $thread = $this->threadRecord;
        $thread['data'] = array('name' => $details['name'], 'hall' =>
                $details['hall'], 'lock' => 0, 'status' => 0, 'lastResponse' =>
                time(), 'createPostsAllowed' =>
                $hall['data']['createPostsAllowed'], 'editPostsAllowed' =>
                $hall['data']['editPostsAllowed'], 'deletePostsAllowed' =>
                $hall['data']['deletePostsAllowed']);
        $this->dbapi->save($thread);
        $user = &Modules::getModule('Users')->getUser();
        $post = $this->postRecord;
        $post['data'] = array('thread' => $thread['data']['id'], 'author' =>
                $user['data']['id'], 'title' => $details['name'], 'created' =>
                time(), 'lastEdit' => 0, 'text' => $details['text'],
                'editCount' => 0);
        $this->dbapi->save($post);
        Kernel::setRedirect($this->getLink('thread/' . Kernel::createUrl(
                $thread['data']['name']) . '-' . $thread['data']['id'] . '/'));
    }

    public function onCreatePost(&$details, $source) {
        if (!$this->canCreatePost($details['thread'])) {
            return;
        }
        $thread = $this->threadRecord;
        $thread['data']['id'] = $details['thread'];
        $this->dbapi->refresh($thread);
        $post = $this->postRecord;
        $author = &Modules::getModule('Users')->getUser();
        $post['data'] = array('thread' => $details['thread'], 'author' =>
                $author['data']['id'], 'title' => $details['name'],
                'created' => time(), 'lastEdit' => 0, 'text' =>
                $details['text'], 'editCount' => 0);
        $this->dbapi->save($post);
        $thread['data']['lastResponse'] = $post['data']['created'];
        $this->dbapi->update($thread);
        Kernel::setRedirect($this->getLink('thread/' . Kernel::createUrl(
                $thread['data']['name']) . '-' . $thread['data']['id'] . '/'));
    }

    public function onSearch(&$details, $source) {
        Kernel::setRedirect($this->getLink('search/' .
                urlencode($details['search']) . '/'));
    }

    public function getMetaInfo($property, $url) {
        $url = explode('/', $url);
        if (isset($url[1])) {
            if ($url[1] == 'watched') {
                return 'Sledovaná vlákna na fóru';
            } elseif ($url[1] == 'my-posts') {
                return 'Moje příspěvky na fóru';
            } elseif ($url[1] == 'rules') {
                return 'Pravidla fóra';
            } elseif ($url[1] == 'help') {
                return 'Fórum - Nápověda';
            } elseif (($url[1] == 'hall') && isset($url[2])) {
                $url = explode('-', $url[2]);
                $url = $url[count($url) - 1];
                $hall = $this->hallRecord;
                $hall['data']['id'] = $url;
                $this->dbapi->refresh($hall);
                return 'Fórum - ' . $hall['data']['name'];
            } elseif (($url[1] == 'thread') && isset($url[2])) {
                $url = explode('-', $url[2]);
                $url = $url[count($url) - 1];
                $thread = $this->threadRecord;
                $thread['data']['id'] = $url;
                $this->dbapi->refresh($thread);
                return isset($thread['data']['name']) ?
                        'Fórum - ' . $thread['data']['name'] :
                        'Fórum - vlákno nenalezeno';
            }
        }
        return 'Fórum';
    }

    public function displayPublicWatched($page = 1) {
        $user = Modules::getModule('Users')->getUser();
        $pages = array('COUNT(DISTINCT(`thread`))');
        $pages = &$this->dbdriver->select(self::POSTS, $pages, '`author` = ' .
                $user['data']['id']);
        $pages = ceil($pages[0]['COUNT(DISTINCT(`thread`))'] /
                $this->threadsOnPage);
        $threads = array('DISTINCT(`thread`)');
        $order = array('id');
        $threads = &$this->dbdriver->select(self::POSTS, $threads,
                '`author` = ' . $user['data']['id'], $order, ($page - 1) *
                $this->threadsOnPage, $this->threadsOnPage);
        foreach ($threads as &$thread) {
            $thread = $thread['thread'];
        }
        if (count($threads) > 0) {
            $threads = array('filter' => '`id` IN (' .
                    implode(', ', $threads) . ')', '');
            $threads = &$this->recordList->getList($this->threadRecord,
                    $threads);
        } else {
            $threads = array();
        }
        foreach ($threads as &$thread) {
            $thread['link'] = $this->getLink('thread/' .
                    Kernel::createUrl($thread['name']) . '-' . $thread['id'] .
                    '/');
            $hall = $this->hallRecord;
            $hall['data']['id'] = $thread['hall'];
            $this->dbapi->refresh($hall);
            $thread['hall'] = $hall['data']['name'];
            $thread['hallLink'] = $this->getLink('hall/' .
                    Kernel::createUrl($hall['data']['name']) . '-' .
                    $hall['data']['id'] . '/');
            $thread['icon'] = Modules::getModule('Files')->
                    getFile($hall['data']['icon']);
            $thread['icon'] = Config::ROOT . '/modules/Files/uploaded/' .
                    $thread['icon']['data']['localName'];
        }
        $params = array('threads' => &$threads, 'paging' =>
                array('linkPrefix' => $this->getLink('watched/'),
                'linkSuffix' => '/', 'current' => $page, 'count' => $pages));
        $this->template('forum-watched', $params);
    }

    public function displayPublicMyPosts($page = 1) {
        $user = Modules::getModule('Users')->getUser();
        $pages = array('COUNT(*)');
        $pages = &$this->dbdriver->select(self::POSTS, $pages, '`author` = ' .
                $user['data']['id']);
        $pages = ceil($pages[0]['COUNT(*)'] / $this->postsOnPage);
        $page = max(min($page, $pages), 1);
        $params = array('filter' => '`author` = ' . $user['data']['id'],
                'start' => ($page - 1) * $this->postsOnPage, 'count' =>
                $this->postsOnPage, 'order' => array('!id'));
        $posts = &$this->recordList->getList($this->postRecord, $params);
        foreach ($posts as &$post) {
            $thread = $this->threadRecord;
            $thread['data']['id'] = $post['thread'];
            $this->dbapi->refresh($thread);
            $post['created'] = date("d.m.Y H:i:s", $post['created']);
            $post['thread'] = $thread['data']['name'];
            $post['threadLink'] = $this->getLink('thread/' .
                    Kernel::createUrl($post['thread']) . '-' .
                    $thread['data']['id'] . '/');
        }
        $params = array('posts' => &$posts, 'paging' => array('linkPrefix' =>
                $this->getLink('my-posts/'), 'linkSuffix' => '/', 'current' =>
                $page, 'count' => $pages));
        $this->template('forum-my-posts', $params);
    }

    public function displayPublicCreatePost($threadId) {
        if (!$this->canCreatePost($threadId)) {
            Modules::getModule('Visitors')->displayPublicLogin(null);
            return;
        }
        $thread = $this->threadRecord;
        $thread['data']['id'] = $threadId;
        $this->dbapi->refresh($thread);
        $params = array('form' => array('start' => $this->getForm(
                'createPost', true, 'Forum'), 'submit' => 'create',
                'items' => array(array('name' => 'name', 'label' =>
                'postTitle'), array('name' => 'thread', 'type' =>
                'hidden', 'value' => $threadId), array('name' => 'text',
                'type' => 'textarea', 'label' => 'postText', 'cols' => 80,
                'rows' => 5))));
        $this->prepareFormData($params['form']);
        $this->template('forum-create-post', $params);
    }

    public function displayPublicSearch($searchPhrase, $page = 1) {
        $cols = array('COUNT(*)');
        $pageCount = &$this->dbdriver->select(self::POSTS, $cols,
                "`text` LIKE '%" . $this->dbdriver->escapeString(
                $searchPhrase) . "%'");
        $pageCount = ceil($pageCount[0][$cols[0]] / $this->postsOnPage);
        $page = $page < 1 ? 1 : ($page > $pageCount ? $pageCount : $page);
        $params = array('order' => array('!id'), 'filter' => "`text` LIKE '%" .
                $this->dbdriver->escapeString($searchPhrase) . "%'", 'start' =>
                ($page - 1) * $this->postsOnPage, 'count' =>
                $this->postsOnPage);
        $posts = &$this->recordList->getList($this->postRecord, $params);
        foreach ($posts as &$post) {
            $post['author'] = &Modules::getModule('Users')->
                    getUser($post['author']);
            $post['authorLink'] = Config::WEB_DIR . '/visitors/user/' .
                    Kernel::createUrl($post['author']['data']['login']) . '-' .
                    $post['author']['data']['id'] . '/';
            $post['author'] = $post['author']['data']['login'];
        }
        $params = array('listOfHallsLink' => $this->getLink(),
                'contactAdminLink' => $this->getLink('contact-admin/'),
                'rulesLink' => $this->getLink('rules/'), 'helpLink' =>
                $this->getLink('help/'), 'formStart' => $this->getForm(
                'search', true, 'Forum'), 'pages' => array('linkPrefix' =>
                $this->getLink('search/' . urlencode($searchPhrase) . '/'),
                'linkSuffix' => '/', 'current' => $page, 'count' =>
                $pageCount), 'posts' => $posts, 'searchPhrase' =>
                $searchPhrase);
        $this->template('forum-search', $params);
    }

    public function displayPublicThread($threadId, $page = 1) {
        $threadId = substr($threadId, strrpos($threadId, '-') + 1);
        $thread = $this->threadRecord;
        $thread['data']['id'] = $threadId;
        $this->dbapi->refresh($thread);
        if (!isset($thread['data']['name'])) {
            return;
        }
        $cols = array('COUNT(*)');
        $pageCount = &$this->dbdriver->select(self::POSTS, $cols, '`thread` ' .
                '= ' . (int) $threadId);
        $pageCount = ceil($pageCount[0][$cols[0]] / $this->postsOnPage);
        $page = (int) $page;
        $page = $page < 1 ? 1 : ($page > $pageCount ? $pageCount : $page);
        $params = array('order' => array('id'), 'filter' =>
                "`thread` = {$thread['data']['id']}", 'start' => ($page - 1) *
                $this->postsOnPage, 'count' => $this->postsOnPage);
        $posts = &$this->recordList->getList($this->postRecord, $params);
        foreach ($posts as &$post) {
            $post['author'] = &Modules::getModule('Users')->
                    getUser($post['author']);
            $post['authorLink'] = Config::WEB_DIR . '/visitors/user/' .
                    Kernel::createUrl($post['author']['data']['login']) . '-' .
                    $post['author']['data']['id'] . '/';
            $post['author'] = $post['author']['data']['login'];
        }
        $hall = $this->hallRecord;
        $hall['data']['id'] = $thread['data']['hall'];
        $this->dbapi->refresh($hall);
        $params = array('thread' => $thread['data'], 'posts' => $posts,
                'allowedCreateThread' => $this->canCreateThread(
                $thread['data']['hall']), 'allowedCreatePost' => $this->
                canCreatePost($threadId), 'listOfHallsLink' => $this->
                getLink(), 'listOfThreadsLink' => $this->getLink('hall/' .
                Kernel::createUrl($hall['data']['name']) . '-' .
                $hall['data']['id'] . '/'), 'newThreadLink' => $this->getLink(
                'create-thread/' . $hall['data']['id'] . '/'), 'newPostLink' =>
                $this->getLink('create-post/' . $threadId . '/'),
                'contactAdminLink' => $this->getLink('contact-admin/'),
                'rulesLink' => $this->getLink('rules/'), 'helpLink' =>
                $this->getLink('help/'), 'formStart' => $this->getForm(
                'search', true, 'Forum'), 'pages' => array('linkPrefix' =>
                $this->getLink('thread/' . Kernel::createUrl(
                $thread['data']['name']) . '-' . $thread['data']['id'] . '/'),
                'linkSuffix' => '/', 'current' => $page, 'count' =>
                $pageCount));
        $this->template('forum-thread', $params);
    }

    public function displayPublicHall($hall, $page = 1) {
        $hallId = substr($hall, strrpos($hall, '-') + 1);
        if (!$this->canAccessHall($hallId)) {
            return;
        }
        $hall = $this->hallRecord;
        $hall['data']['id'] = $hallId;
        $this->dbapi->refresh($hall);
        if (!isset($hall['data']['name'])) {
            return;
        }
        $cols = array('COUNT(*)');
        $pageCount = &$this->dbdriver->select(self::THREADS, $cols, '`hall` ' .
                '= ' . (int) $hall['data']['id']);
        $pageCount = ceil($pageCount[0][$cols[0]] / $this->threadsOnPage);
        $page = max(1, min($page, $pageCount));
        $params = array('order' => array('!status', '!lastResponse'),
                'start' => ($page - 1) * $this->threadsOnPage, 'count' =>
                $this->threadsOnPage, 'filter' => '`hall` = ' .
                $hall['data']['id']);
        $threads = &$this->recordList->getList($this->threadRecord, $params);
        $cols = array('COUNT(*)');
        foreach ($threads as &$thread) {
            $thread['link'] = $this->getLink('thread/' . Kernel::createUrl(
                    $thread['name']) . '-' . $thread['id'] . '/');
            $responses = &$this->dbdriver->select(self::POSTS, $cols,
                    '`thread` = ' . $thread['id']);
            $thread['responses'] = $responses[0]['COUNT(*)'];
            $thread['responses'] = $thread['responses'] > 1 ?
                    $thread['responses'] - 1 : '-';
            $params = array('count' => 1, 'order' => array('id'), 'filter' =>
                    '`thread` = ' . $thread['id']);
            $thread['author'] = &$this->recordList->getList($this->postRecord,
                    $params);
            $thread['author'] = $thread['author'][0]['author'];
            $author = &Modules::getModule('Users')->getUser($thread['author']);
            $thread['author'] = $author['data']['login'];
            $thread['authorlink'] = Config::WEB_DIR . '/visitors/user/' .
                    Kernel::createUrl($author['data']['login']) . '-' .
                    $author['data']['id'] . '/';
        }
        $params = array('hall' => $hall['data'], 'listOfHallsLink' =>
                $this->getLink(), 'newThreadLink' => $this->getLink(
                'create-thread/' . $hall['data']['id'] . '/'),
                'contactAdminLink' => $this->getLink('contact-admin/'),
                'rulesLink' => $this->getLink('rules/'), 'helpLink' =>
                $this->getLink('help/'), 'formStart' => $this->getForm(
                'search', true, 'Forum'), 'allowedCreateThread' =>
                $this->canCreateThread($hall['data']['id']), 'threads' =>
                $threads, 'pages' => array('linkPrefix' => $this->getLink(
                'hall/' . Kernel::createUrl($hall['data']['name']) . '-' .
                $hall['data']['id'] . '/'), 'linkSuffix' => '/', 'current' =>
                $page, 'count' => $pageCount));
        $this->template('forum-hall', $params);
    }

    public function displayPublicCreateThread($hallId) {
        $hall = $this->hallRecord;
        $hall['data']['id'] = $hallId;
        $this->dbapi->refresh($hall);
        if (!$this->canCreateThread($hallId)) {
            Modules::getModule('Visitors')->displayPublicLogin(null);
            $this->addMsg('cannotCreateThread', self::MSG_ERROR);
            return;
        }
        $params = array('form' => array('start' => $this->getForm(
                'createThread', true, 'Forum'), 'submit' => 'create',
                'items' => array(array('name' => 'name', 'label' =>
                'threadNameForm'), array('name' => 'hall', 'type' => 'hidden',
                'value' => $hallId), array('name' => 'text', 'type' =>
                'textarea', 'label' => 'threadText', 'cols' => 80, 'rows' =>
                5))));
        $this->prepareFormData($params['form']);
        $this->template('forum-create-thread', $params);
    }

    public function displayPublicHelp() {
        $params = array();
        $this->template('forum-help', $params);
    }

    public function displayPublicRules() {
        $params = array();
        $this->template('forum-rules', $params);
    }

    public function displayPublicContactAdmin() {
        $params = array('form' => array('start' => $this->getForm(
                'contactAdmin', true, 'Forum'), 'submit' => 'send', 'items' =>
                array(array('name' => 'title', 'label' => 'contactTitle'),
                array('name' => 'text', 'label' => 'messageText', 'type' =>
                'textarea', 'cols' => 80, 'rows' => 6))));
        $this->prepareFormData($params['form']);
        $this->template('forum-contact-admin', $params);
    }

    public function displayPublicIndex() {
        $halls = &$this->getIndexHallsList();
        foreach ($halls as &$hall) {
            $hall['link'] = $this->getLink('hall/' .
                    Kernel::createUrl($hall['name']) . "-{$hall['id']}/");
            $hall['allowed'] = $this->canAccessHall($hall['id']);
            if ($hall['icon'] > 0) {
                $hall['icon'] = Modules::getModule('Files')->
                        getFile($hall['icon']);
                $hall['icon'] = Config::ROOT . '/modules/Files/uploaded/' .
                        $hall['icon']['data']['localName'];
            }
        }
        $params = array('formStart' => $this->getForm('search', true,
                'Forums'), 'allowedCreateHall' => $this->canCreateHall(),
                'createHallLink' => $this->getLink('create-hall/'), 'halls' =>
                $halls);
        $this->template('forum-index', $params);
    }

    public function displayPublicCreateHall() {
        $params = array('form' => array('start' => $this->getForm('createHall',
                true, 'Forum'), 'submit' => 'create', 'items' => array(array(
                'name' => 'name', 'label' => 'hallName'), array('name' =>
                'description', 'label' => 'hallDescription'), array('name' =>
                'parent', 'type' => 'select', 'options' =>
                $this->getHallsFormList(), 'label' => 'hallParent'), array(
                'name' => 'lock', 'label' => 'locked', 'type' => 'checkbox',
                'value' => 1), array('name' => 'createPostsAllowed', 'label' =>
                'createPostsAllowed', 'type' => 'checkbox', 'value' => 1,
                'checked' => true), array('name' => 'editPostsAllowed',
                'label' => 'editPostsAllowed', 'type' => 'checkbox', 'value' =>
                1), array('name' => 'deletePostsAllowed', 'label' =>
                'deletePostsAllowed', 'type' => 'checkbox', 'value' => 1))));
        $this->prepareFormData($params['form']);
        $this->template('forum-create-hall', $params);
    }

    private function getHallsFormList() {
        $params = array('order' => array('id'));
        $halls = &$this->recordList->getList($this->hallRecord, $params);
        $list = array(0 => '-');
        foreach ($halls as $hall) {
            $list[$hall['id']] = $hall['name'];
        }
        return $list;
    }

    private function &getIndexHallsList($parent = 0) {
        $params = array('order' => array('id'), 'filter' => '`parent` = ' .
                $parent);
        $halls = &$this->recordList->getList($this->hallRecord, $params);
        $result = array();
        foreach ($halls as &$hall) {
            $result[] = $hall;
            $sub = &$this->getIndexHallsList($hall['id']);
            $result = array_merge($result, $sub);
        }
        return $result;
    }

    private function canCreatePost($threadId) {
        if ($this->hasRights('create post')) {
            return true;
        }
        $user = &Modules::getModule('Users')->getUser();
        if ($user['data']['login'] == '<guest>') {
            return false;
        }
        $thread = $this->threadRecord;
        $thread['data']['id'] = $threadId;
        $this->dbapi->refresh($thread);
        return ($thread['data']['lock'] == 0) &&
                ($thread['data']['createPostsAllowed'] == 1);
    }

    private function canAccessHall($hallId) {
        if ($this->hasRights('create thread')) {
            return true;
        }
        $hall = $this->hallRecord;
        $hall['data']['id'] = $hallId;
        $this->dbapi->refresh($hall);
        return $hall['data']['createPostsAllowed'] == 1;
    }

    private function canCreateThread($hallId) {
        if ($this->hasRights('create thread')) {
            return true;
        }
        $user = &Modules::getModule('Users')->getUser();
        if ($user['data']['login'] == '<guest>') {
            return false;
        }
        $hall = $this->hallRecord;
        $hall['data']['id'] = $hallId;
        $this->dbapi->refresh($hall);
        return ($hall['data']['lock'] == 0) &&
                ($hall['data']['createPostsAllowed'] == 1);
    }

    private function canCreateHall() {
        return $this->hasRights('create hall');
    }
}
?>
